/*
 * Copyright 2010 David de Mingo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.shalma.client.ui;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.shalma.client.ui.icon.ShalmaResources;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;

public class MagicButton extends Widget implements ClickListener {

	public static final String STYLE = "shalma_presentation-MagicButton-";

	private static final int EVENTS = Event.ONMOUSEMOVE | Event.ONMOUSEOUT
			| Event.ONMOUSEUP;

	private static final int IMAGE_TIME = 300;

	private static final int MAX_DTIME = 800;

	private static final int DELTA = 5;

	private int ox, oy;

	private long time;

	private Object userObject;

	private boolean done;

	private Image inactive;

	private ConfirmPanel confirmPanel;

	private List controls;

	private List listeners;

	// ///

	public MagicButton(MagicButtonListener listener) {
		this(new Image(ShalmaResources.instance.button()), listener);
	}

	public MagicButton(Image image, MagicButtonListener listener) {
		super();
		inactive = image;
		setElement(inactive.getElement());
		sinkEvents(Event.ONMOUSEDOWN);
		if (listener != null)
			addListener(listener);
	}

	// ///

	public Object getUserObject() {
		return userObject;
	}

	/**
	 * Sets the user-defined object associated with this button.
	 * 
	 * @param userObject
	 *            the button's user-defined object
	 */
	public void setUserObject(Object userObject) {
		this.userObject = userObject;
	}

	public void addControl(MagicButtonControl control) {
		if (controls == null)
			controls = new ArrayList();
		controls.add(control);
	}

	public void addListener(MagicButtonListener listener) {
		if (listeners == null)
			listeners = new ArrayList();
		listeners.add(listener);
	}

	public void removeListener(MagicButtonListener listener) {
		if (listeners != null)
			listeners.remove(listener);
	}

	// ///

	public void onBrowserEvent(Event event) {

		int eventType = DOM.eventGetType(event);

		if (Event.ONMOUSEDOWN == eventType) {
			done = false;
			time = System.currentTimeMillis();
			ox = DOM.eventGetClientX(event);
			oy = DOM.eventGetClientY(event);
			sinkEvents(EVENTS);
			setImage(new Image(ShalmaResources.instance.button_active()));
			return;
		}

		if (done)
			return;

		switch (eventType) {
		case Event.ONMOUSEMOVE:
			mouseMove(event);
			break;
		case Event.ONMOUSEUP:
			mouseUp(event);
		case Event.ONMOUSEOUT:
			done();
			restore();
			break;
		}
	}

	public void onClick(Widget sender) {

		if (confirmPanel != null) {
			confirmPanel.hide();
			MagicButtonAction action = confirmPanel.action;

			fireEvent(action);
			confirmPanel = null;
		}
	}

	// ///

	void mouseMove(Event event) {

		int x = DOM.eventGetClientX(event);
		int y = DOM.eventGetClientY(event);
		int dx = Math.abs(x - ox);
		int dy = Math.abs(y - oy);

		if (dx > DELTA && dy < DELTA) {
			if ((x - ox) > 0)
				mouseMoveSelection(MagicButtonAction.RIGHT);
			else
				mouseMoveSelection(MagicButtonAction.LEFT);
		} else if (dy > DELTA && dx < DELTA) {
			if ((y - oy) > 0) {
				mouseMoveSelection(MagicButtonAction.DOWN);
			} else {
				mouseMoveSelection(MagicButtonAction.UP);
			}
		}
	}

	void mouseUp(Event event) {

		long dtime = System.currentTimeMillis() - time;

		if (dtime < MAX_DTIME) {

			int dx = Math.abs(DOM.eventGetClientX(event) - ox);
			int dy = Math.abs(DOM.eventGetClientY(event) - oy);

			if (dx < 2 && dy < 2)
				fireEvent(MagicButtonAction.CLICK);
		}
	}

	// ///

	private void done() {
		done = true;
		unsinkEvents(EVENTS);
	}

	private void fireEvent(MagicButtonAction action) {

		for (Iterator it = listeners.iterator(); it.hasNext();)
			((MagicButtonListener) it.next()).onEvent(this, action);
	}

	private void mouseMoveSelection(MagicButtonAction action) {

		done();

		if (action == MagicButtonAction.DOWN)
			showImageAction(new Image(ShalmaResources.instance.button_down()));
		else if (action == MagicButtonAction.LEFT)
			showImageAction(new Image(ShalmaResources.instance.button_left()));
		else if (action == MagicButtonAction.RIGHT)
			showImageAction(new Image(ShalmaResources.instance.button_right()));
		else if (action == MagicButtonAction.UP)
			showImageAction(new Image(ShalmaResources.instance.button_up()));

		if (controls == null)
			return;

		for (Iterator iterator = controls.iterator(); iterator.hasNext();) {
			MagicButtonControl control = (MagicButtonControl) iterator.next();
			if (control.enabled(action)) {
				if (control.confirm(action)) {
					confirmPanel = new ConfirmPanel(
							control.makeDescriptor(action), this);
					confirmPanel.action = action;
					confirmPanel.show();
					confirmPanel.center();
				} else
					fireEvent(action);
				break;
			}
		}
	}

	private void showImageAction(Image image) {

		setImage(image);
		Timer timer = new Timer() {
			public void run() {
				restore();
			}
		};
		timer.schedule(IMAGE_TIME);
	}

	private void setImage(Image image) {
		setElement(image.getElement());
	}

	private void restore() {
		if (inactive.getElement() != getElement())
			setImage(inactive);
	}

	// ///

	static class ConfirmPanel extends PopupPanel {

		private MagicButton button;

		MagicButtonAction action;

		// ///

		ConfirmPanel(Widget descriptor, MagicButton button) {
			super(true);
			HorizontalPanel container = new HorizontalPanel();
			container.setSpacing(4);
			container.add(descriptor);
			Image apply = new Image(ShalmaResources.instance.button_apply());
			apply.addClickListener(button);
			container.add(apply);
			setWidget(container);

			setStyleName(STYLE + "ConfirmPanel");
		}

		// ///

		void position() {

		}
	}
}